/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <list>
#include "server_common.h"
#include "server_thread.h"
#include "server_remoteend.h"

extern ServerConfigure g_serverConfig;
extern DWORD g_acceptRegThreadID, g_acceptHBThreadID, g_messageProcThreadID;

static std::list<RemoteEnd *> g_remoteEndList;

static void showAllRemoteEnds()
{
    std::list<RemoteEnd *>::iterator it = g_remoteEndList.begin();
    int32_t remote_count = 1;
    fflush(stdout);
    g_default_log << tstamp << "Remote List: \n";
    for (; it != g_remoteEndList.end(); ++it)
    {
        g_default_log << tstamp << "\t"<<remote_count++<<\
        ": ["<<(*it)->getUID()<<"] ["<<(*it)->getIP()<<"] ["\
            <<(*it)->getLife()<<"]\n";
    }
}

/*FUNCTION*********************************************************************
 *
 * Function Name : acceptRegister
 * Description   : Accept registration request at port g_serverConfig.portRegister.
 * This function will not stop being blocked at accept() until
 * g_serverConfig.enableAccept is set to false by other threads like main or else.
 *END*************************************************************************/
DWORD WINAPI svr::accept::acceptRegister(void * pM)
{
    /* Create a socket to listen for incoming client connection. */
    SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    /* Fill the local sockaddr_in structure, where listen_socket is bound. */
    sockaddr_in sin;

    /* listen_socket is not created successfully. */
    if (listen_socket == INVALID_SOCKET)
    {
        g_register_log << tstamp << "Error "<<WSAGetLastError()<<" in creating register socket.\n";

        WSACleanup();

        PostThreadMessageA(g_messageProcThreadID, WM_SVR_FAIL, 0, (LPARAM)NULL);

        return 1;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(g_serverConfig.portRegister);

    /* Associates the listen_socket with the address. */
    if (bind(listen_socket, (sockaddr *) &sin, sizeof(sin)) == SOCKET_ERROR)
    {

        g_register_log << tstamp << "Error "<<WSAGetLastError()<<" in binding register socket.\n";

        closesocket(listen_socket);

        WSACleanup();

        PostThreadMessageA(g_messageProcThreadID, WM_SVR_FAIL, 0, (LPARAM)NULL);

        return 1;
    }

    /* Put the socket into listening mode to wait for incoming connections. */
    listen(listen_socket, MAXUID);

    /* Accept until state -> false */
    while (g_serverConfig.enableAccept)
    {
        sockaddr_in client_addr;
        int n_addr_len = sizeof(client_addr);
        /* Hold to accept */
        SOCKET sub_socket = ::accept(listen_socket, (SOCKADDR *) &client_addr, &n_addr_len);

        if (sub_socket == INVALID_SOCKET)
        {
            closesocket(sub_socket);
            continue;
        }

        /* Create a sub thread to handle registration. */
        g_register_log << tstamp << "Accept a register client from IP["<<inet_ntoa(client_addr.sin_addr)<<"].\n";
        CreateThread(NULL, 0,
                    svr::receive::recvRegister,
                    (void *) sub_socket,  /* Pass sub-socket to thread for calling recv()*/
                     0, NULL);

    }
    /* Accept is over */
    closesocket(listen_socket);
    WSACleanup();

    return 0;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : acceptHeartbeat
 * Description   : Accept registration at port g_serverConfig.portHeartbeat.
 * This function works almost like acceptRegister() except for different port
 * used for heartbeat request and different receive() thread for heartbeat message
 * process.
 *END*************************************************************************/
DWORD WINAPI svr::accept::acceptHeartbeat(void * pM)
{
    /* Create a socket to listen for incoming client connection. */
    SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    /* Fill the local sockaddr_in structure, where listen_socket is bound. */
    sockaddr_in sin;

    sin.sin_family = AF_INET;
    sin.sin_port = htons(g_serverConfig.portHeartbeat);

    /* listen_socket is not created successfully. */
    if (listen_socket == INVALID_SOCKET)
    {

        g_heartbeat_log << tstamp << "Error "<<WSAGetLastError()<<" in creating heartbeat socket.\n";

        WSACleanup();

        PostThreadMessageA(g_messageProcThreadID, WM_SVR_FAIL, 0, (LPARAM)NULL);

        return 1;
    }

    /* Associates the listen_socket with the address. */
    if (bind(listen_socket, (sockaddr *) &sin, sizeof(sin)) == SOCKET_ERROR)
    {

        g_heartbeat_log << tstamp << "Error "<<WSAGetLastError()<<" in binding heartbeat socket.\n";

        closesocket(listen_socket);
        WSACleanup();

        PostThreadMessageA(g_messageProcThreadID, WM_SVR_FAIL, 0, (LPARAM)NULL);

        return 1;
    }

    /* Put the socket into listening mode to wait for incoming connections. */
    listen(listen_socket, MAXUID);

    /* Accept until state -> false */
    while (g_serverConfig.enableAccept)
    {
        sockaddr_in client_addr;
        int n_addr_len = sizeof(client_addr);
        /* Hold to accept */
        SOCKET sub_socket = ::accept(listen_socket, (SOCKADDR *) &client_addr, &n_addr_len);

        if (sub_socket == INVALID_SOCKET)
        {
            /* accept error, continue to accept */
            closesocket(sub_socket);
            continue;
        }
        else
        {
            g_heartbeat_log << tstamp << "Accept a heartbeat client from IP["<<inet_ntoa(client_addr.sin_addr)<<"].\n";

            /* Create a sub thread to handle heartbeat. */
            CreateThread(NULL, 0,
                        svr::receive::recvHeartbeat,
                        (void *)sub_socket,  /* Pass sub-socket to thread for calling recv()*/
                         0, NULL);
        }
    }
    /* Accept is over */
    closesocket(listen_socket);
    WSACleanup();

    return 0;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : acceptRequest
 * Description   : Accept request at port g_serverConfig.portRequest.
 *END*************************************************************************/
DWORD WINAPI svr::accept::acceptRequest(void * pM)
{
    /* Create a socket to listen for incoming client connection. */
    SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    /* Fill the local sockaddr_in structure, where listen_socket is bound. */
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(g_serverConfig.portRequest);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;

    /* listen_socket is not created successfully. */
    if (listen_socket == INVALID_SOCKET)
    {

        g_request_log << tstamp << "Error "<<WSAGetLastError()<<" in creating request socket.\n";

        WSACleanup();

        PostThreadMessageA(g_messageProcThreadID, WM_SVR_FAIL, 0, (LPARAM)NULL);

        return 1;
    }
    /* Associates the listen_socket with the address. */
    if (bind(listen_socket, (sockaddr *) &sin, sizeof(sin)) == SOCKET_ERROR)
    {


        g_request_log << tstamp << "Error "<<WSAGetLastError()<<" in binding request socket.\n";

        closesocket(listen_socket);
        WSACleanup();

        PostThreadMessageA(g_messageProcThreadID, WM_SVR_FAIL, 0, (LPARAM)NULL);

        return 1;
    }

    /* Put the socket into listening mode to wait for incoming connections. */
    listen(listen_socket, MAXUID);

    /* Accept until state -> false */
    while (g_serverConfig.enableAccept)
    {
        sockaddr_in client_addr;
        int n_addr_len = sizeof(client_addr);
        /* Hold to accept */
        SOCKET sub_socket = ::accept(listen_socket, (SOCKADDR *) &client_addr, &n_addr_len);

        if (sub_socket == INVALID_SOCKET)
        {
            closesocket(sub_socket);
            continue;
        }
        else
        {
            g_request_log << tstamp << "Accept a request client from IP["\
                            <<inet_ntoa(client_addr.sin_addr)<<"].\n";

            /* Create a sub thread to handle request. */
            CreateThread(NULL, 0,
                        svr::receive::recvRequest,
                        (void *)sub_socket,  /* Pass sub-socket to thread for calling recv()*/
                         0, NULL);
        }
    }
    /* Accept is over */
    closesocket(listen_socket);
    WSACleanup();

    return 0;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : recvRegister
 * Description   : In order to avoid multi-thread file access, this thread is NOT
 * allowed to modify log file. So This thread only receives chars and posts
 * registration information struct pointer *server_reg_info to timer thread.
 * If data received is unvalid, thread itself deletes server_reg_info and exit;
 * If data received is valid, deleting is done in timer thread(DANGEROUS).
 *END*************************************************************************/
DWORD WINAPI svr::receive::recvRegister(void * pM)
{
    SOCKET socket = (SOCKET)pM;
    char uid_buf[UIDLEN+1];
    uint32_t recvNum;
    /* Hold to receive */
    recvNum = ::recv(socket, uid_buf, UIDLEN, 0);

    if (recvNum > 0) /* Receive successfully */
    {
        /* Pack it as struct and post it to timer thread.
         * This memory will be freed in timer thread(valid data)
         * or right after(Unvalid data).
        */
        uid_buf[UIDLEN] = '\0'; // avoid stack overflow

        ServerInfoPack *server_info_pack = new ServerInfoPack();

        sockaddr_in sa;
        int len = sizeof(sa);
        /* Get IP */
        ::getpeername(socket, (sockaddr *) &sa, &len);

        sprintf(server_info_pack->IP, "%s", inet_ntoa(sa.sin_addr));
        sprintf(server_info_pack->UID, "%s", uid_buf);
        ::PostThreadMessageA(g_messageProcThreadID, WM_REG, 0, (LPARAM)server_info_pack);
    }

    /* Close socket */
    ::closesocket(socket);

    return 0;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : recvHeartbeat
 * Description   : Thread using this function takes SOCKET as input parameter
 * which is used to get remote IP addresss in order to get related timerID.
 * After that, function post MSG with timerID attched as lParam to timer thread.
 * Function returns a " " to KABS to inform it if this IP is already registered.
 *END*************************************************************************/
DWORD WINAPI svr::receive::recvHeartbeat(void * pM)
{
    SOCKET socket = (SOCKET)pM;
    char buf[RECV_BUFSIZ]; /* Max receive bytes*/
    uint32_t recvNum = 0;
    sockaddr_in sa;
    int len = sizeof(sa);

    recvNum = recv(socket, buf, 512, 0);

    if (recvNum > 0)
    {
		ServerInfoPack *server_info_pack = new ServerInfoPack();

        getpeername(socket, (sockaddr *) &sa, &len);
		sprintf(server_info_pack->IP, "%s", inet_ntoa(sa.sin_addr));
		server_info_pack->s = socket;
        /* Post timerID to timer thread to reset the timer */
		PostThreadMessageA(g_messageProcThreadID, WM_HB, 0, (LPARAM)server_info_pack);
    }

    return 0;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : recvRequest
 * Description   : Go through every alive Timer and its UID accordingly, read
 * log.ini to find IP info. If IP is not found, a "-" will be returned.
 *END*************************************************************************/
DWORD WINAPI svr::receive::recvRequest(void * pM)
{
    SOCKET socket = (SOCKET)pM;
    uint32_t recvNum;
    char buf[BUFSIZ];
    char full_path[BUFSIZ];

    sprintf(full_path, ".\\%s", g_serverConfig.logFileName);
    /* Hold to receive */
    recvNum = ::recv(socket, buf, BUFSIZ, 0);

    if (recvNum > 0) /* Receive successfully */
    {
		ServerInfoPack *server_info_pack = new ServerInfoPack();

		sprintf(server_info_pack->INFO, "%s", buf);
		server_info_pack->s = socket;
		PostThreadMessageA(g_messageProcThreadID, WM_REQ, 0, (LPARAM)server_info_pack);
    }

    return 0;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : messageProc
 * Description   : Thread using this function deals with various Messages:
 * WM_CRTTMR: Create a timer according to registration information struct pointer
 *            passed in as lParam.
 *            If there's a timer, reset it and update <IP, timerID>
 *            If there's no timer, create one and update two related maps.
 * WM_RSTTMR: Reset a timer according to timerID passed in as wParam.
 *            This timerID has been checked so function simply reset it.
 * WM_TIMER: Time out. Function uses timerID to find MAC to update STATE. Function
 *           also erase related information in two global maps.
 * WM_SVR_FAIL: Server has to shutdown and this message will terminate this thread,
 * which causes main thread to exit.
 * NOTE: ONLY this function is ALLOWED to modify log file.
 *END*************************************************************************/
DWORD WINAPI svr::message::messageProc(void * pM)
{
    MSG Msg;
    /* Modify kabs_log.ini */
    char full_path[BUFSIZ];

    sprintf(full_path, ".\\%s", g_serverConfig.logFileName);

    while (GetMessage(&Msg, NULL, 0, 0))
    {
        switch(Msg.message)
        {
        case WM_REG: /* Register */
        {
            ServerInfoPack *server_info_pack = (ServerInfoPack *)Msg.lParam;
            std::list<RemoteEnd *>::iterator it = g_remoteEndList.begin();
            /* Scan all alive remote ends to see if this is the first register*/
            for (; it != g_remoteEndList.end(); ++it)
            {
                // found: re-register
                if (strcmp((*it)->getUID(), server_info_pack->UID) == 0)
                {
                    (*it)->refresh();
                    (*it)->updateIP(server_info_pack->IP);

                    fsl::LogStream log_stream(server_info_pack->UID);
                    log_stream << tstamp << server_info_pack->UID<<" re-register.\n";
                    break;
                }
            }
            // not found: first register
            if (it == g_remoteEndList.end())
            {
                g_remoteEndList.push_back(new RemoteEnd(server_info_pack->UID,
                                                        server_info_pack->IP,
                                                        g_serverConfig.timeout,
                                                        g_messageProcThreadID));

                fsl::LogStream log_stream(server_info_pack->UID);
                log_stream << tstamp << server_info_pack->UID<< " register.\n";
            }
            /* Update log.ini */
            ::WritePrivateProfileStringA(server_info_pack->UID, "IP",    server_info_pack->IP,   full_path);
            ::WritePrivateProfileStringA(server_info_pack->UID, "STATE", "1",            full_path);
            char temp[BUFSIZ];
            ::GetPrivateProfileStringA(server_info_pack->UID,
                                            "INFO", "-", temp, BUFSIZ,
                                            full_path);
            if (strcmp(temp, "-") == 0)
            {
                ::WritePrivateProfileStringA(server_info_pack->UID, "INFO", " ",            full_path);
            }
            /* Delete memory allocated in recv thread*/
            delete server_info_pack;
            break;
        }
        case WM_HB: /* Refresh remote */
        {
			ServerInfoPack *server_info_pack = (ServerInfoPack *)Msg.lParam;
			std::list<RemoteEnd *>::iterator it = g_remoteEndList.begin();
			for (; it != g_remoteEndList.end(); it++)
			{
				// found instance, echo a char
				if (strcmp((*it)->getIP(), server_info_pack->IP) == 0)
				{
					(*it)->refresh();
					send(server_info_pack->s, " ", 2, 0);

                    fsl::LogStream log_stream((*it)->getUID());
                    log_stream << tstamp << (*it)->getUID() <<" send a valid heartbeat.\n";
				}
			}
			closesocket(server_info_pack->s);
			delete server_info_pack;

            break;
        }
		case WM_TMUP: /* Delete remote */
		{
			RemoteEnd *re = (RemoteEnd *)Msg.lParam;
			::WritePrivateProfileStringA(re->getUID(), "STATE", "0", full_path);

            fsl::LogStream log_stream((re->getUID()));
            log_stream << tstamp << re->getUID() << " is off-line.\n";
            // list will call ~re()
			g_remoteEndList.remove(re);
            // this free memory allocated for re
			delete re;
			break;
		}
		case WM_REQ: /* Return request information*/
		{
			ServerInfoPack *server_info_pack = (ServerInfoPack *)Msg.lParam;
			std::list<RemoteEnd *>::iterator it = g_remoteEndList.begin();
			/* Search every remote */
			for (; it != g_remoteEndList.end(); ++it)
			{
				char temp[64];
				::GetPrivateProfileStringA((*it)->getUID(),
                                            "INFO", "-", temp, 64,
                                            full_path);
				if (strcmp(temp, server_info_pack->INFO) == 0) /* Found, send ip */
				{
					::GetPrivateProfileStringA((*it)->getUID(),
                                                "IP", "---.---.---.---",
                                                temp, 64,
                                                full_path);
					send(server_info_pack->s, temp, 64, 0);
					break;
				}
			}
			/* Close socket */
			::closesocket(server_info_pack->s);
			delete server_info_pack;
			break;
		}
        case WM_SVR_FAIL:
        {
            g_serverConfig.enableAccept = false;
            return 1;
        }
        default:
            break;
        }
        showAllRemoteEnds();
        DispatchMessage(&Msg);
    }
    return 0;
}
/* EOF */
